import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import { DatabaseConnection } from '@/database/connection';
import { AuthenticationError, AuthorizationError } from './errorHandler';
import { Logger } from '@/utils/logger';

const logger = new Logger('AuthMiddleware');

interface JWTPayload {
  userId: string;
  username: string;
  iat: number;
  exp: number;
}

interface AuthenticatedUser {
  id: string;
  username: string;
  phone: string;
  email?: string;
  status: string;
}

declare global {
  namespace Express {
    interface Request {
      user?: AuthenticatedUser;
    }
  }
}

export class AuthMiddleware {
  private static jwtSecret = process.env.JWT_SECRET || 'default-secret-key';
  private static jwtExpiresIn = process.env.JWT_EXPIRES_IN || '7d';

  /**
   * 生成JWT令牌
   */
  public static generateToken(user: { id: string; username: string }): string {
    try {
      const payload = {
        userId: user.id,
        username: user.username
      };

      return jwt.sign(payload, this.jwtSecret, {
        expiresIn: this.jwtExpiresIn,
        issuer: 'health-assistant',
        audience: 'health-assistant-users'
      });
    } catch (error) {
      logger.error('Failed to generate JWT token', { error: error.message, userId: user.id });
      throw new Error('Token generation failed');
    }
  }

  /**
   * 生成刷新令牌
   */
  public static generateRefreshToken(): string {
    const crypto = require('crypto');
    return crypto.randomBytes(32).toString('hex');
  }

  /**
   * 验证JWT令牌
   */
  public static verifyToken(token: string): JWTPayload {
    try {
      return jwt.verify(token, this.jwtSecret, {
        issuer: 'health-assistant',
        audience: 'health-assistant-users'
      }) as JWTPayload;
    } catch (error) {
      if (error instanceof jwt.TokenExpiredError) {
        throw new AuthenticationError('访问令牌已过期');
      } else if (error instanceof jwt.JsonWebTokenError) {
        throw new AuthenticationError('无效的访问令牌');
      } else {
        logger.error('Token verification failed', { error: error.message });
        throw new AuthenticationError('令牌验证失败');
      }
    }
  }

  /**
   * 身份验证中间件
   */
  public static authenticate = async (
    req: Request,
    res: Response,
    next: NextFunction
  ): Promise<void> => {
    try {
      const authHeader = req.headers.authorization;
      
      if (!authHeader) {
        throw new AuthenticationError('缺少访问令牌');
      }

      const token = authHeader.startsWith('Bearer ') 
        ? authHeader.substring(7) 
        : authHeader;

      if (!token) {
        throw new AuthenticationError('访问令牌格式无效');
      }

      // 验证JWT令牌
      const payload = this.verifyToken(token);
      
      // 从数据库获取用户信息
      const db = DatabaseConnection.getConnection();
      const user = await db('users')
        .select('id', 'username', 'phone', 'email', 'status')
        .where('id', payload.userId)
        .first();

      if (!user) {
        throw new AuthenticationError('用户不存在');
      }

      if (user.status !== 'active') {
        throw new AuthenticationError('用户账户已被禁用');
      }

      // 将用户信息附加到请求对象
      req.user = user;

      // 更新最后登录时间
      await db('users')
        .where('id', user.id)
        .update({ last_login_at: new Date() });

      logger.debug('User authenticated successfully', { 
        userId: user.id, 
        username: user.username,
        requestId: req.requestId 
      });

      next();
    } catch (error) {
      if (error instanceof AuthenticationError) {
        next(error);
      } else {
        logger.error('Authentication failed', { 
          error: error.message, 
          requestId: req.requestId 
        });
        next(new AuthenticationError('身份验证失败'));
      }
    }
  };

  /**
   * 可选身份验证中间件（不强制要求认证）
   */
  public static optionalAuthenticate = async (
    req: Request,
    res: Response,
    next: NextFunction
  ): Promise<void> => {
    try {
      const authHeader = req.headers.authorization;
      
      if (!authHeader) {
        return next();
      }

      const token = authHeader.startsWith('Bearer ') 
        ? authHeader.substring(7) 
        : authHeader;

      if (!token) {
        return next();
      }

      // 尝试验证令牌，失败也不抛出错误
      try {
        const payload = this.verifyToken(token);
        
        const db = DatabaseConnection.getConnection();
        const user = await db('users')
          .select('id', 'username', 'phone', 'email', 'status')
          .where('id', payload.userId)
          .where('status', 'active')
          .first();

        if (user) {
          req.user = user;
        }
      } catch (error) {
        // 静默忽略认证错误
        logger.debug('Optional authentication failed', { 
          error: error.message, 
          requestId: req.requestId 
        });
      }

      next();
    } catch (error) {
      // 如果出现其他错误，也继续执行
      logger.warn('Optional authentication error', { 
        error: error.message, 
        requestId: req.requestId 
      });
      next();
    }
  };

  /**
   * 权限检查中间件
   */
  public static requirePermission = (permission: string) => {
    return async (req: Request, res: Response, next: NextFunction): Promise<void> => {
      try {
        if (!req.user) {
          throw new AuthenticationError('需要身份验证');
        }

        // 这里可以扩展为基于角色的权限系统
        // 目前简单实现，所有认证用户都有基本权限
        const userPermissions = ['read:own', 'write:own', 'update:own'];
        
        if (!userPermissions.includes(permission)) {
          throw new AuthorizationError(`缺少权限: ${permission}`);
        }

        next();
      } catch (error) {
        next(error);
      }
    };
  };

  /**
   * 检查用户是否为资源所有者
   */
  public static requireOwnership = (userIdField: string = 'userId') => {
    return async (req: Request, res: Response, next: NextFunction): Promise<void> => {
      try {
        if (!req.user) {
          throw new AuthenticationError('需要身份验证');
        }

        const targetUserId = req.params[userIdField] || req.body[userIdField] || req.query[userIdField];
        
        if (!targetUserId) {
          throw new AuthorizationError('无法确定资源所有者');
        }

        if (req.user.id !== targetUserId) {
          throw new AuthorizationError('只能访问自己的资源');
        }

        next();
      } catch (error) {
        next(error);
      }
    };
  };

  /**
   * 管理员权限检查
   */
  public static requireAdmin = async (
    req: Request,
    res: Response,
    next: NextFunction
  ): Promise<void> => {
    try {
      if (!req.user) {
        throw new AuthenticationError('需要身份验证');
      }

      // 这里可以从数据库查询用户角色
      // 目前简单实现，可以通过环境变量配置管理员用户
      const adminUsers = (process.env.ADMIN_USERS || '').split(',');
      
      if (!adminUsers.includes(req.user.username) && !adminUsers.includes(req.user.id)) {
        throw new AuthorizationError('需要管理员权限');
      }

      next();
    } catch (error) {
      next(error);
    }
  };

  /**
   * 刷新令牌验证
   */
  public static async verifyRefreshToken(
    token: string, 
    userId: string
  ): Promise<boolean> {
    try {
      const db = DatabaseConnection.getConnection();
      const crypto = require('crypto');
      const tokenHash = crypto.createHash('sha256').update(token).digest('hex');

      const refreshToken = await db('refresh_tokens')
        .where('token_hash', tokenHash)
        .where('user_id', userId)
        .where('expires_at', '>', new Date())
        .first();

      if (!refreshToken) {
        return false;
      }

      // 更新最后使用时间
      await db('refresh_tokens')
        .where('id', refreshToken.id)
        .update({ last_used_at: new Date() });

      return true;
    } catch (error) {
      logger.error('Refresh token verification failed', { 
        error: error.message, 
        userId 
      });
      return false;
    }
  }

  /**
   * 保存刷新令牌
   */
  public static async saveRefreshToken(
    userId: string, 
    token: string, 
    deviceInfo?: string
  ): Promise<void> {
    try {
      const db = DatabaseConnection.getConnection();
      const crypto = require('crypto');
      const tokenHash = crypto.createHash('sha256').update(token).digest('hex');

      // 设置过期时间（30天）
      const expiresAt = new Date();
      expiresAt.setDate(expiresAt.getDate() + 30);

      await db('refresh_tokens').insert({
        user_id: userId,
        token_hash: tokenHash,
        expires_at: expiresAt,
        device_info: deviceInfo,
        created_at: new Date(),
        updated_at: new Date()
      });

      logger.debug('Refresh token saved', { userId, deviceInfo });
    } catch (error) {
      logger.error('Failed to save refresh token', { 
        error: error.message, 
        userId 
      });
      throw error;
    }
  }

  /**
   * 清除过期的刷新令牌
   */
  public static async cleanupExpiredTokens(): Promise<void> {
    try {
      const db = DatabaseConnection.getConnection();
      const deletedCount = await db('refresh_tokens')
        .where('expires_at', '<', new Date())
        .del();

      if (deletedCount > 0) {
        logger.info(`Cleaned up ${deletedCount} expired refresh tokens`);
      }
    } catch (error) {
      logger.error('Failed to cleanup expired tokens', { error: error.message });
    }
  }
}